C#: Extract indexer when accessing a span with a range.#21877
C#: Extract indexer when accessing a span with a range.#21877michaelnebel wants to merge 3 commits into
Conversation
4eec760 to
9167814
Compare
There was a problem hiding this comment.
Pull request overview
This PR improves the C# extractor’s handling of element-access expressions that use range syntax (for example, a[0..3]) when Roslyn lowers them into method calls (for example, Slice(...)). The goal is to preserve the source-level “indexer access” shape by still recording an indexer as the call target, improving database-quality telemetry.
Changes:
- Added extractor special-casing to recover an indexer symbol even when Roslyn binds the element access to a method symbol.
- Updated the Telemetry/DatabaseQuality query test inputs and expected outputs to reflect the improved extraction.
- Added a C# library change note documenting the analysis-impacting extraction improvement.
Show a summary per file
| File | Description |
|---|---|
| csharp/ql/test/query-tests/Telemetry/DatabaseQuality/Quality.cs | Updates test code to treat range element access as properly-targeted indexer calls. |
| csharp/ql/test/query-tests/Telemetry/DatabaseQuality/NoTarget.expected | Removes expected “missing call target” results now that targets are extracted. |
| csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected | Eliminates expected “not ok” call-target results for the fixed cases. |
| csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md | Documents the extraction change as a minor analysis improvement. |
| csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs | Implements the new symbol recovery logic for range-based element access. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 1
| if (symbol is IMethodSymbol method) | ||
| { | ||
| var indexers = method | ||
| .ContainingType | ||
| .GetMembers() | ||
| .OfType<IPropertySymbol>() | ||
| .Where(p => p.IsIndexer); | ||
|
|
||
| var intIndexer = indexers | ||
| .Where(i => i.Parameters.Length == 1 && i.Parameters[0].Type.SpecialType == SpecialType.System_Int32) | ||
| .FirstOrDefault(); | ||
|
|
||
| return intIndexer; | ||
| } |
There was a problem hiding this comment.
I think it is reasonable to assume that Roslyn only translates a[range] into a method call for integer range expressions (and thus we implicitly want to extract the indexer with an int parameter).
hvitved
left a comment
There was a problem hiding this comment.
I'm not convinced that extracting the indexer is what we want; what about extracting the Roslyn-resolved targets instead and treating the index expressions as ordinary method calls?
Yes, I also considered that, but thought it would be better to extract something that more closely resembles the syntax instead of what it is being translated into. If I were to query our code and asking for uses of indexers, then I would expect cases like |
Improves the extraction of span range-access expressions (such as
a[0..3]).Roslyn translates
a[0..3]intoa.Slice(0,3)for spans (and something similar for strings). In this PR we introduce some special handling in the extractor for such cases and still considera[0..3]as indexer calls (as this most closely resembles the syntax).